1 module hip.hipaudio.backend.sles; 2 import hip.error.handler; 3 import hip.console.log; 4 import hip.util.conv:to; 5 import core.atomic; 6 7 version(Android): 8 import opensles.android; 9 import opensles.sles; 10 /** 11 * OpenSL ES Debuggability 12 */ 13 package __gshared SLresult[] sliErrorQueue; 14 package __gshared string[] sliErrorMessages; 15 16 ///Packs engine interface, object and capabilities and give a cleaner interface for use 17 struct SLIEngine 18 { 19 SLObjectItf engineObject = null; 20 SLEngineItf engine; 21 SLEngineCapabilitiesItf engineCapabilities; 22 bool willUseFastMixer; 23 24 void initialize() 25 { 26 SLEngineOption engineOptions = SLEngineOption(SL_ENGINEOPTION_THREADSAFE, SL_BOOLEAN_TRUE); 27 sliCall(slCreateEngine(&engineObject,1,&engineOptions,0,null,null), 28 "Could not create engine"); 29 30 //Initialize|Realize the engine 31 sliCall((*engineObject).Realize(engineObject, SL_BOOLEAN_FALSE), 32 "Could not realize|initialize engine"); 33 34 35 //Get the interface for being able to create child objects from the engine 36 sliCall((*engineObject).GetInterface(engineObject, SL_IID_ENGINE, &engine), 37 "Could not get an interface for creating objects"); 38 39 if(sliCall((*engineObject).GetInterface(engineObject, SL_IID_ENGINECAPABILITIES, &engineCapabilities), 40 "Could not get engine capabilities")) 41 { 42 if(sliCall((*engineCapabilities).QueryAPIVersion(engineCapabilities, &engineMajor, &engineMinor, &enginePatch), 43 "Could not query OpenSLES version")) 44 rawlog("OpenSL Version: "~to!string(engineMajor)~"."~to!string(engineMinor)~"."~to!string(enginePatch)); 45 else if(sliErrorMessages.length == 1) 46 sliClearErrors(); 47 } 48 else if(sliErrorMessages.length == 1) 49 sliClearErrors(); 50 } 51 52 uint CreateOutputMix(const(const(SLObjectItf_)*)** outputMix, uint interfacesCount, 53 const(const(SLInterfaceID_)*)* interfaces, const(uint)* requirements) 54 { 55 return (*engine).CreateOutputMix(engine, outputMix, interfacesCount, interfaces, requirements); 56 } 57 uint CreateAudioPlayer(SLObjectItf* audioPlayer, SLDataSource* source, SLDataSink* sink, 58 uint interfacesCount, SLInterfaceID* interfaces, uint* requirements) 59 { 60 return (*engine).CreateAudioPlayer(engine, audioPlayer, source, sink, interfacesCount, interfaces, requirements); 61 } 62 63 void Destroy() 64 { 65 if(engineObject != null) 66 { 67 (*engineObject).Destroy(engineObject); 68 } 69 } 70 } 71 72 /** 73 * Engine related objects 74 */ 75 package __gshared SLIEngine engine; 76 package __gshared short engineMajor = 1; 77 package __gshared short engineMinor = 0; 78 package __gshared short enginePatch = 1; 79 80 /** 81 * Controls the output and the players 82 */ 83 package __gshared SLIOutputMix outputMix; 84 package __gshared SLIAudioPlayer*[] genPlayers; 85 86 87 88 string sliGetError(SLresult res) 89 { 90 switch (res) 91 { 92 case SL_RESULT_SUCCESS: return "Success"; 93 case SL_RESULT_BUFFER_INSUFFICIENT: return "Buffer insufficient"; 94 case SL_RESULT_CONTENT_CORRUPTED: return "Content corrupted"; 95 case SL_RESULT_CONTENT_NOT_FOUND: return "Content not found"; 96 case SL_RESULT_CONTENT_UNSUPPORTED: return "Content unsupported"; 97 case SL_RESULT_CONTROL_LOST: return "Control lost"; 98 case SL_RESULT_FEATURE_UNSUPPORTED: return "Feature unsupported"; 99 case SL_RESULT_INTERNAL_ERROR: return "Internal error"; 100 case SL_RESULT_IO_ERROR: return "IO error"; 101 case SL_RESULT_MEMORY_FAILURE: return "Memory failure"; 102 case SL_RESULT_OPERATION_ABORTED: return "Operation aborted"; 103 case SL_RESULT_PARAMETER_INVALID: return "Parameter invalid"; 104 case SL_RESULT_PERMISSION_DENIED: return "Permission denied"; 105 case SL_RESULT_PRECONDITIONS_VIOLATED: return "Preconditions violated"; 106 case SL_RESULT_RESOURCE_ERROR: return "Resource error"; 107 case SL_RESULT_RESOURCE_LOST: return "Resource lost"; 108 case SL_RESULT_UNKNOWN_ERROR: return "Unknown error"; 109 default: return "Undefined error"; 110 } 111 } 112 113 package void sliClearErrors() 114 { 115 sliErrorMessages.length = 0; 116 sliErrorQueue.length = 0; 117 } 118 119 120 bool sliError(SLresult res, lazy string errMessage, string file = __FILE__, string func = __PRETTY_FUNCTION__, uint line = __LINE__) 121 { 122 if(res != SL_RESULT_SUCCESS) 123 { 124 sliErrorQueue~= res; 125 rawerror("'OpenSL ES' Error: '"~sliGetError(res)~"' at file "~file~":"~to!string(line)~ " at "~func~"\n\t"~errMessage); 126 } 127 return res != SL_RESULT_SUCCESS; 128 } 129 130 /** 131 * Calls OpenSL ES function, checks if there was error, and append the message on the messagesqueue 132 * when an error occurs 133 * 134 * Returns wether the call was okay 135 */ 136 private bool sliCall(SLresult opRes, string errMessage, 137 string file = __FILE__, string func = __PRETTY_FUNCTION__, uint line = __LINE__) 138 { 139 if(sliError(opRes, errMessage, file, func, line)) 140 { 141 sliErrorMessages~= errMessage; 142 return false; 143 } 144 145 return true; 146 } 147 148 149 struct SLIOutputMix 150 { 151 SLEnvironmentalReverbItf environmentReverb; 152 SLPresetReverbItf presetReverb; 153 SLBassBoostItf bassBoost; 154 SLEqualizerItf equalizer; 155 SLVirtualizerItf virtualizer; 156 SLObjectItf outputMixObj; 157 158 159 static bool initializeForAndroid(ref SLIOutputMix output, ref SLIEngine e, bool willUseFastMixer) 160 { 161 //All those interfaces are supported on Android, so, require them 162 const(SLInterfaceID)* ids = null; 163 const(SLboolean)* req = null; 164 uint count = 0; 165 166 if(!willUseFastMixer) 167 { 168 ids = 169 [ 170 SL_IID_ENVIRONMENTALREVERB, 171 SL_IID_PRESETREVERB, 172 SL_IID_BASSBOOST, 173 SL_IID_EQUALIZER, 174 SL_IID_VIRTUALIZER 175 ].ptr; 176 177 req = 178 [ 179 SL_BOOLEAN_TRUE, 180 SL_BOOLEAN_TRUE, 181 SL_BOOLEAN_TRUE, 182 SL_BOOLEAN_TRUE, 183 SL_BOOLEAN_TRUE //5 184 ].ptr; 185 count = 5; 186 } 187 188 with(output) 189 { 190 sliCall(e.CreateOutputMix(&outputMixObj, count, ids, req), 191 "Could not create output mix"); 192 //Do it assyncly 193 sliCall((*outputMixObj).Realize(outputMixObj, SL_BOOLEAN_FALSE), 194 "Could not initialize output mix"); 195 196 197 if(!willUseFastMixer) 198 { 199 if(!sliCall((*outputMixObj).GetInterface(outputMixObj, SL_IID_ENVIRONMENTALREVERB, &environmentReverb), 200 "Could not get the ENVIRONMENTALREVERB interface")) 201 environmentReverb = null; 202 203 if(!sliCall((*outputMixObj).GetInterface(outputMixObj, SL_IID_PRESETREVERB, &presetReverb), 204 "Could not get the PRESETREVERB interface")) 205 presetReverb = null; 206 207 if(!sliCall((*outputMixObj).GetInterface(outputMixObj, SL_IID_BASSBOOST, &bassBoost), 208 "Could not get the BASSBOOST interface")) 209 bassBoost = null; 210 211 if(!sliCall((*outputMixObj).GetInterface(outputMixObj, SL_IID_EQUALIZER, &equalizer), 212 "Could not get the EQUALIZER interface")) 213 equalizer = null; 214 215 if(!sliCall((*outputMixObj).GetInterface(outputMixObj, SL_IID_VIRTUALIZER, &virtualizer), 216 "Could not get the VIRTUALIZER interface")) 217 virtualizer = null; 218 } 219 } 220 return sliErrorQueue.length == 0; 221 } 222 } 223 224 225 float sliToAttenuation(float gain) 226 { 227 import std.math:log10; 228 return (gain < 0.01f) ? -96.0f : 20 * log10(gain); 229 } 230 231 const(SLInterfaceID)[] getAudioPlayerInterfaces(bool willUseFastMixer) 232 { 233 const(SLInterfaceID)[] ret = [SL_IID_VOLUME, SL_IID_ANDROIDSIMPLEBUFFERQUEUE/*, SL_IID_MUTESOLO*/]; //can't require SL_IID_MUTESOLO with a mono buffer queue data source error 234 if(!willUseFastMixer) 235 { 236 ret~= [ 237 SL_IID_BASSBOOST, 238 SL_IID_EFFECTSEND, 239 SL_IID_ENVIRONMENTALREVERB, 240 SL_IID_EQUALIZER, 241 SL_IID_PLAYBACKRATE, 242 SL_IID_PRESETREVERB, 243 SL_IID_VIRTUALIZER, 244 SL_IID_ANDROIDEFFECT, 245 SL_IID_ANDROIDEFFECTSEND, 246 SL_IID_METADATAEXTRACTION 247 ][]; 248 } 249 return ret; 250 } 251 SLboolean[] getAudioPlayerRequirements(ref const(SLInterfaceID)[] itfs) 252 { 253 SLboolean[] ret; 254 foreach (const(SLInterfaceID) id; itfs) 255 ret~= SL_BOOLEAN_TRUE; 256 return ret; 257 } 258 259 string sliGetErrorMessages() 260 { 261 string ret; 262 for(int i = 0; i < sliErrorMessages.length;i++) 263 ret~= sliGetError(sliErrorQueue[i])~": "~sliErrorMessages[i]; 264 265 sliErrorQueue.length = 0; 266 sliErrorMessages.length = 0; 267 return ret; 268 } 269 270 ///Must be used as an opaque pointer 271 struct SLIBuffer 272 { 273 size_t size; 274 bool isLocked; 275 bool hasBeenProcessed; 276 ///Tightly packed structure 277 void[0] data; 278 } 279 280 /** 281 * Creates an unresizable buffer(tightly packed) for not getting a cache miss 282 */ 283 SLIBuffer* sliGenBuffer(void[] data, size_t size) 284 { 285 import core.stdc.stdlib:malloc; 286 import core.stdc.string:memcpy,memset; 287 SLIBuffer* buf = cast(SLIBuffer*)malloc(SLIBuffer.sizeof+size); 288 buf.size = size; 289 buf.isLocked = false; 290 buf.hasBeenProcessed = false; 291 if(data == null) 292 memset(buf.data.ptr, 0, size); 293 else 294 memcpy(buf.data.ptr, data.ptr, size); 295 return buf; 296 } 297 298 ///Copies data inside the buffer on its immutable size. Use that on unlocked buffers. 299 void sliBufferData(SLIBuffer* buffer, void[] data) 300 { 301 import core.stdc.string:memcpy; 302 ErrorHandler.assertExit(!buffer.isLocked, "Can't write to locked buffer"); 303 memcpy(buffer.data.ptr, data.ptr, buffer.size); 304 } 305 306 ///Invalidates the buffer and makes it null 307 void sliDestroyBuffer(ref SLIBuffer* buff) 308 { 309 import core.stdc.stdlib:free; 310 if(buff != null) 311 free(buff); 312 buff = null; 313 } 314 315 __gshared struct SLIAudioPlayer 316 { 317 ///The Audio player 318 SLObjectItf playerObj; 319 ///Play/stop/pause the audio 320 SLPlayItf player; 321 ///Controls the volume 322 SLVolumeItf playerVol; 323 ///Ability to get and set the audio duration 324 SLSeekItf playerSeek; 325 326 SLBassBoostItf bassBoost; 327 SLEnvironmentalReverbItf envReverb; 328 SLEqualizerItf equalizer; 329 SLPlaybackRateItf playbackRate; 330 SLPresetReverbItf presetReverb; 331 SLVirtualizerItf virtualizer; 332 SLAndroidEffectItf androidEffect; 333 SLAndroidEffectSendItf androidEffectSend; 334 335 ///TODO: 336 SLEffectSendItf playerEffectSend; 337 ///TODO: 338 SLMetadataExtractionItf playerMetadata; 339 340 struct EnqueuedBuffer 341 { 342 void* data; 343 size_t size; 344 } 345 346 EnqueuedBuffer enqueued; 347 348 /** 349 * This queue works as: 350 351 In Use Unqueued Capacity 352 |1, 2, 3, 4| /5, 6\ (0, 0, 0, 0) 353 */ 354 protected SLIBuffer** streamQueue; 355 ///Data between | | 356 protected ushort streamQueueLength; 357 ///Data between / \ 358 protected ushort streamQueueFree; 359 //Data between ( ) 360 protected ushort streamQueueCapacity; 361 ///How many chunks have been streamed to that player 362 protected ushort totalChunksEnqueued; 363 ///How many chunks have been played 364 protected ushort totalChunksPlayed; 365 366 367 SLAndroidSimpleBufferQueueItf playerAndroidSimpleBufferQueue; 368 SLIBuffer* nextBuffer; 369 bool isPlaying, hasFinishedTrack, isLooping; 370 371 float volume; 372 373 void update() 374 { 375 if(nextBuffer != null) 376 { 377 //Need to clear queue, as having too many can cause a crash 378 SLIAudioPlayer.Clear(this); 379 //Set it playing 380 SLIAudioPlayer.Enqueue(this, nextBuffer.data.ptr, nextBuffer.size); 381 nextBuffer = null; 382 } 383 else if(hasFinishedTrack) 384 { 385 if(isLooping) 386 { 387 if(enqueued != EnqueuedBuffer.init) 388 { 389 SLIAudioPlayer.stop(this); 390 SLIAudioPlayer.Enqueue(this, enqueued.data, enqueued.size); 391 SLIAudioPlayer.play(this); 392 } 393 else 394 loglnError("Tried to loop OpenSLES AudioPlayer, but there is no enqueued buffer"); 395 } 396 else 397 { 398 // logln("STOPPED!"); 399 SLIAudioPlayer.stop(this); 400 } 401 } 402 } 403 404 static void setVolume(ref SLIAudioPlayer audioPlayer, float gain) 405 { 406 with(audioPlayer) 407 { 408 (*playerVol).SetVolumeLevel(playerVol, cast(SLmillibel)(sliToAttenuation(gain)*100)); 409 volume = gain; 410 } 411 } 412 413 static void setRate(ref SLIAudioPlayer audioPlayer, float rate) 414 { 415 if(rate < 0.5 || rate > 2.0) 416 { 417 import hip.util.conv:to; 418 ErrorHandler.showErrorMessage("Unsupported rate change on OpenSL ES.", "Max rate change is from 0.5 to 2.0, received "~rate.to!string); 419 } 420 short newRate = cast(short)(rate * 1000); 421 with(audioPlayer) 422 { 423 sliCall((*playbackRate).SetRate(playbackRate, newRate), "Could not set playback rate"); 424 } 425 } 426 427 /** 428 * Also invalidates enqueued SLIBuffer, so, destroy with care 429 */ 430 static void destroyAudioPlayer(ref SLIAudioPlayer audioPlayer) 431 { 432 with(audioPlayer) 433 { 434 (*playerObj).Destroy(playerObj); 435 if(streamQueue != null) 436 { 437 for(int i = 0; i < streamQueueLength; i++) 438 sliDestroyBuffer(streamQueue[i]); 439 streamQueue = null; 440 streamQueueLength = 0; 441 } 442 playerObj = null; 443 player = null; 444 playerVol = null; 445 playerSeek = null; 446 playerEffectSend = null; 447 playerAndroidSimpleBufferQueue = null; 448 } 449 } 450 alias PlayerCallback = extern(C) void function(SLPlayItf player, void* context, SLuint32 event); 451 452 void RegisterCallback(PlayerCallback callback, void* context) 453 { 454 (*player).RegisterCallback(player, callback, context); 455 } 456 void SetCallbackEventsMask(uint mask) 457 { 458 (*player).SetCallbackEventsMask(player, mask); 459 } 460 461 /** 462 * Checking some bug issues tracker: 463 * - https://groups.google.com/g/android-ndk/c/zANdS2n2cQI 464 * 465 * - https://issuetracker.google.com/issues/37011991 466 * 467 * It seems that you can't make any call to OpenSL API inside SL_PLAYEVENT_HEADATEND 468 * 469 * I'm delegating it right now to the void update() method. As it seems, there is a little 470 * more music playing after SL_PLAYEVENT_HEADATEND(it is notified early). 471 * 472 * It can cause some unsync in some other device, but that must be checked case-to-case. 473 * 474 * Using ushort.max seems to be the way to go about how much it need to decode. ushort.max/4 caused some 475 * interruptions in music, while ushort.max/8 was inaudible (ushort.max is a great number, 65k) 476 * 477 * It is also possible to bypass the need for a callback by calling GetState and checking if count == 0, 478 * that will mean that the head is at the end 479 */ 480 extern(C) static void checkStreamCallback(SLPlayItf player, void* context, SLuint32 event) 481 { 482 if(event & SL_PLAYEVENT_HEADATEND) 483 { 484 import hip.console.log; 485 SLIAudioPlayer* p = (cast(SLIAudioPlayer*)context); 486 if(p.streamQueueLength > 0) 487 { 488 SLIBuffer* buf; 489 if(p.totalChunksPlayed == 0) 490 buf = p.streamQueue[0]; 491 else 492 { 493 p.unqueue(p.streamQueue[0]); //Unqueue the old buffer, thus, streamQueueLength-- 494 if(p.streamQueueLength > 0) 495 buf = p.streamQueue[0]; 496 } 497 if(buf != null) 498 { 499 buf.isLocked = true; 500 p.totalChunksPlayed++; 501 p.nextBuffer = buf; 502 } 503 } 504 else 505 { 506 import hip.console.log; 507 logln("Finished track on AudioThread"); 508 p.hasFinishedTrack = true; 509 } 510 } 511 } 512 513 void pushBuffer(SLIBuffer* buffer) 514 { 515 import core.stdc.stdlib:malloc,realloc; 516 517 totalChunksEnqueued++; 518 streamQueueLength++; 519 ushort totalSize = cast(ushort)(streamQueueLength + streamQueueFree); 520 521 if(streamQueue == null) 522 streamQueue = cast(SLIBuffer**)malloc((SLIBuffer*).sizeof * totalSize); 523 else if(totalSize > streamQueueCapacity) 524 { 525 streamQueue = cast(SLIBuffer**)realloc(streamQueue, (SLIBuffer*).sizeof * totalSize); 526 streamQueueCapacity = totalSize; 527 } 528 buffer.hasBeenProcessed = false; 529 //Buffer is locked when playing 530 streamQueue[streamQueueLength-1] = buffer; 531 } 532 533 /** 534 * Gets a free buffer from the /\ list 535 */ 536 SLIBuffer* getProcessedBuffer() 537 { 538 for(int i = streamQueueLength; i < streamQueueLength+streamQueueFree;i++) 539 { 540 if(!streamQueue[i].isLocked && streamQueue[i].hasBeenProcessed) 541 return streamQueue[i]; 542 } 543 return null; 544 } 545 /** 546 * Will remove the free buffer and set it as unused /b\ -> (0) 547 * 548 * - | | = Enqueued 549 * 550 * - / \ = Free 551 * 552 * - ( ) = Unused (capacity) 553 */ 554 void removeFreeBuffer(SLIBuffer* freeBuffer) 555 { 556 ErrorHandler.assertExit(!freeBuffer.isLocked, "This buffer is being used right now"); 557 bool isReordering = false; 558 for(int i = streamQueueLength; i < streamQueueLength+streamQueueFree;i++) 559 { 560 if(streamQueue[i] == freeBuffer) 561 isReordering = true; 562 if(isReordering && i+1 < streamQueueCapacity) 563 streamQueue[i] = streamQueue[i+1]; 564 } 565 streamQueueFree--; 566 ErrorHandler.assertExit(isReordering, "OpenSL ES: Buffer sent to remove is not in queue"); 567 } 568 int GetState() 569 { 570 SLAndroidSimpleBufferQueueState res; 571 (*playerAndroidSimpleBufferQueue).GetState(playerAndroidSimpleBufferQueue, &res); 572 573 return res.count; //If 0, nothing is playing 574 } 575 576 /** 577 * Same behavior from (*androidBufferQueue).Enqueue. If you wish to use queue 578 * for streaming sound, call pushBuffer 579 */ 580 static void Enqueue(ref SLIAudioPlayer audioPlayer, void* samples, size_t sampleSize) 581 { 582 assert(sampleSize <= uint.max, "Probably something bad will happen with that size."); 583 584 audioPlayer.enqueued = EnqueuedBuffer(samples, sampleSize); 585 586 (*audioPlayer.playerAndroidSimpleBufferQueue) 587 .Enqueue(audioPlayer.playerAndroidSimpleBufferQueue, samples, cast(uint)sampleSize); 588 } 589 static void Clear(ref SLIAudioPlayer audioPlayer) 590 { 591 (*audioPlayer.playerAndroidSimpleBufferQueue) 592 .Clear(audioPlayer.playerAndroidSimpleBufferQueue); 593 } 594 595 /** 596 * Will put the processed buffer into the free list |b| -> /b\ 597 * 598 * - | | = Enqueued 599 * 600 * - / \ = Free 601 * 602 * - ( ) = Unused (capacity) 603 */ 604 void unqueue(SLIBuffer* processedBuffer) 605 { 606 bool isReordering = false; 607 for(int i = 0; i < streamQueueLength;i++) 608 { 609 if(streamQueue[i] == processedBuffer) 610 isReordering = true; 611 if(isReordering && i+1 < streamQueueCapacity) 612 streamQueue[i] = streamQueue[i+1]; 613 } 614 processedBuffer.hasBeenProcessed = true; 615 processedBuffer.isLocked = false; 616 streamQueueLength--; 617 streamQueue[streamQueueLength+streamQueueFree] = processedBuffer; 618 streamQueueFree++; 619 ErrorHandler.assertExit(isReordering, "SLES Error: buffer not found when trying to unqueue it"); 620 } 621 622 static void resume(ref SLIAudioPlayer audioPlayer) 623 { 624 with(audioPlayer) 625 { 626 isPlaying = true; 627 uint playState; 628 (*player).GetPlayState(player, &playState); 629 630 if(playState == SL_PLAYSTATE_PAUSED || playState == SL_PLAYSTATE_STOPPED) 631 (*player).SetPlayState(player, SL_PLAYSTATE_PLAYING); 632 } 633 } 634 635 static void play(ref SLIAudioPlayer audioPlayer) 636 { 637 with(audioPlayer) 638 { 639 SLIAudioPlayer.resume(audioPlayer); 640 totalChunksEnqueued = 0; 641 totalChunksPlayed = 0; 642 hasFinishedTrack = false; 643 } 644 } 645 static void stop(ref SLIAudioPlayer audioPlayer) 646 { 647 with(audioPlayer) 648 { 649 (*player).SetPlayState(player, SL_PLAYSTATE_STOPPED); 650 SLIAudioPlayer.Clear(audioPlayer); 651 isPlaying = false; 652 } 653 } 654 655 static void pause(ref SLIAudioPlayer audioPlayer) 656 { 657 with(audioPlayer) 658 { 659 (*player).SetPlayState(player, SL_PLAYSTATE_PAUSED); 660 isPlaying = false; 661 } 662 } 663 664 static void seekClipPosition(ref SLIAudioPlayer audioPlayer, float posMillis, SLuint32 seekMode = SL_SEEKMODE_FAST) 665 { 666 with(audioPlayer) 667 { 668 (*playerSeek).SetPosition(playerSeek, cast(SLmillisecond)posMillis, seekMode); 669 } 670 } 671 672 // static void setLoop(ref SLIAudioPlayer audioPlayer, bool shouldLoop, float loopStartMillis = 0, float loopEnd = -1) 673 // { 674 // with(audioPlayer) 675 // { 676 // if(playerSeek is null) 677 // return; 678 // SLmillisecond end = cast(SLmillisecond)loopEnd; 679 // if(loopEnd <= 0) 680 // end = SL_TIME_UNKNOWN; 681 // (*playerSeek).SetLoop(playerSeek, shouldLoop, cast(SLmillisecond)loopStartMillis, end); 682 // } 683 // } 684 685 static void setLoop(ref SLIAudioPlayer audioPlayer, bool shouldLoop) 686 { 687 audioPlayer.isLooping = shouldLoop; 688 } 689 690 } 691 692 /** 693 * Returns null on failure. 694 */ 695 SLIAudioPlayer* sliGenAudioPlayer(SLDataSource src,SLDataSink dest, bool autoRegisterCallback = true) 696 { 697 SLIAudioPlayer temp; 698 bool willUseFastMixer = engine.willUseFastMixer; 699 with(temp) 700 { 701 const(SLInterfaceID)[] ids = getAudioPlayerInterfaces(willUseFastMixer); 702 SLboolean[] req = getAudioPlayerRequirements(ids); 703 704 705 706 version(OpenSLES1) //Used for SDK < 21. But I won't support unless someone ask. 707 { 708 sliCall(engine.CreateAudioPlayer(&playerObj, &src, &dest, 709 cast(uint)(ids.length), (cast(SLInterfaceID[])ids).ptr, req.ptr), 710 "Could not create AudioPlayer with format: "~to!string(*(cast(SLDataFormat_PCM*)src.pFormat))); 711 } 712 else 713 { 714 sliCall(engine.CreateAudioPlayer(&playerObj, &src, &dest, 715 cast(uint)(ids.length), (cast(SLInterfaceID[])ids).ptr, req.ptr), 716 "Could not create AudioPlayer with format: "~to!string(*(cast(SLAndroidDataFormat_PCM_EX*)src.pFormat))); 717 } 718 719 sliCall((*playerObj).Realize(playerObj, SL_BOOLEAN_FALSE), 720 "Could not initialize AudioPlayer"); 721 722 723 sliCall((*playerObj).GetInterface(playerObj, SL_IID_PLAY, &player), 724 "Could not get play interface for AudioPlayer"); 725 sliCall((*playerObj).GetInterface(playerObj, SL_IID_VOLUME, &playerVol), 726 "Could not get volume interface for AudioPlayer"); 727 728 729 if(!willUseFastMixer) 730 { 731 732 // sliCall((*playerObj).GetInterface(playerObj, SL_IID_SEEK, &playerSeek), 733 // "Could not get Seek interface for AudioPlayer"); 734 735 //Metadata 736 sliCall((*playerObj).GetInterface(playerObj, SL_IID_METADATAEXTRACTION, &playerMetadata), 737 "Could not get MetadataExtraction interface for AudioPlayer"); 738 739 //Misc 740 sliCall((*playerObj).GetInterface(playerObj, SL_IID_PLAYBACKRATE, &playbackRate), 741 "Could not get PlaybackRate interface for AudioPlayer"); 742 sliCall((*playerObj).GetInterface(playerObj, SL_IID_VIRTUALIZER, &virtualizer), 743 "Could not get Virtualizer interface for AudioPlayer"); 744 745 //Wave amplitude modifiers 746 sliCall((*playerObj).GetInterface(playerObj, SL_IID_BASSBOOST, &bassBoost), 747 "Could not get BassBoost interface for AudioPlayer"); 748 749 sliCall((*playerObj).GetInterface(playerObj, SL_IID_EQUALIZER, &equalizer), 750 "Could not get Equalizer interface for AudioPlayer"); 751 752 //Reverb 753 sliCall((*playerObj).GetInterface(playerObj, SL_IID_ENVIRONMENTALREVERB, &envReverb), 754 "Could not get EnvironmentalReverb interface for AudioPlayer"); 755 sliCall((*playerObj).GetInterface(playerObj, SL_IID_PRESETREVERB, &presetReverb), 756 "Could not get PresetReverb interface for AudioPlayer"); 757 758 //Effect 759 sliCall((*playerObj).GetInterface(playerObj, SL_IID_EFFECTSEND, &playerEffectSend), 760 "Could not get EffectSend interface for AudioPlayer"); 761 sliCall((*playerObj).GetInterface(playerObj, SL_IID_ANDROIDEFFECT, &androidEffect), 762 "Could not get AndroidEffect interface for AudioPlayer"); 763 764 765 sliCall((*playerObj).GetInterface(playerObj, SL_IID_ANDROIDEFFECTSEND, &androidEffectSend), 766 "Could not get AndroidEffectSend interface for AudioPlayer"); 767 } 768 769 version(Android) 770 { 771 sliCall((*playerObj).GetInterface(playerObj, 772 SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &playerAndroidSimpleBufferQueue), 773 "Could not get AndroidSimpleBufferQueue for AudioPlayer"); 774 } 775 } 776 if(sliErrorMessages.length == 0) 777 { 778 SLIAudioPlayer* playerOut = new SLIAudioPlayer(); 779 *playerOut = temp; 780 if(autoRegisterCallback) 781 { 782 temp.RegisterCallback(&SLIAudioPlayer.checkStreamCallback, cast(void*)playerOut); 783 temp.SetCallbackEventsMask(SL_PLAYEVENT_HEADATEND); 784 } 785 genPlayers~= playerOut; 786 return playerOut; 787 } 788 return null; 789 } 790 791 void sliDestroyContext() 792 { 793 import core.stdc.stdlib; 794 engine.Destroy(); 795 with(outputMix) (*outputMixObj).Destroy(outputMixObj); 796 797 foreach(ref gp; genPlayers) 798 { 799 SLIAudioPlayer.destroyAudioPlayer(*gp); 800 destroy(gp); 801 gp = null; 802 } 803 } 804 805 806 807 bool sliCreateOutputContext( 808 bool hasProAudio=false, 809 bool hasLowLatencyAudio=false, 810 int optimalBufferSize=4096, 811 int optimalSampleRate=44_100, 812 bool willUseFastMixer = false 813 ) 814 { 815 engine = SLIEngine(null,null,null, willUseFastMixer); 816 engine.initialize(); 817 818 // loadSawtooth(); 819 820 SLIOutputMix.initializeForAndroid(outputMix, engine, willUseFastMixer); 821 // SLIAudioPlayer.initializeForAndroid(gAudioPlayer, engine, src, destination); 822 823 return sliErrorQueue.length == 0; 824 }